package jannex;

import jannex.internal.ResultRow;
import jannex.mapping.Column;
import jannex.mapping.Entity;
import jannex.mapping.MappingContext;
import jannex.sql.JxResultSet;
import org.junit.Before;
import org.junit.Test;

import java.sql.Date;
import java.util.Arrays;
import java.util.List;

import static org.junit.Assert.assertTrue;


public class MappingExamples {

    private MappingContext context;
    private Jannex jannex;

    @Before
    public void createJannex() {
        this.jannex = new JannexBuilder("jdbc:hsqldb:mem:mymemdb").credentials("SA", "").build();
        this.context = jannex.getMappingContext();

    }

    public <E> void testResult(E expected, JxResultSet resultSet) {
        resultSet.next();
        @SuppressWarnings("unchecked")
        Class<E> expectedClass = (Class<E>) expected.getClass();
        E e = context.getHandler(expectedClass).handle(resultSet);
        assertTrue("Expected " + TestUtils.toString(expected) + " but was " +
                TestUtils.toString(e), TestUtils.deepEquals(expected, e));
    }

    public <E> void testResult(E expected, String createTable, String insert, String select) {
        jannex.query(createTable).execute();
        jannex.query(insert).execute();
        try (JxResultSet resultSet = jannex.query(select).resultSet()) {
            testResult(expected, resultSet);
        }
    }

    @Test
    public void basic() {
        testResult(new Object() {
                       int id = 1;
                       String name = "test";
                   },
                "create table basic (id int generated by default as identity (start with 1), name varchar(200))",
                "insert into basic (name) values ('test')",
                "select * from basic");
    }

    @Test
    public void joined() {
        class Local {
            int id = 2;
            String name = "local";
        }
        class Joined {
            int id = 1;
            String name = "test";
            Local innerThingy = new Local();
        }
        testResult(new Joined(), new ResultRow(
                new Object[]{2, "local", 1, "test"},
                new String[]{"ID", "NAME", "ID", "NAME"},
                new String[]{"local", "local", "Joined", "Joined"}
        ));
    }

    @Test
    public void complexTypes() {
        @Entity("multiple_types")
        class ComplexKey {
            @Column(insertable = false)
            int thatId = 1;
            @Column(insertable = false)
            int otherId = 2;
        }

        @Entity(value = "multiple_types", uniqueConstraints = {"thatId", "otherId"})
        class Complex {
            ComplexKey key = new ComplexKey();
            String field = "value";
            List<Object> someArray = Arrays.<Object>asList("value1", "value2");
            Date created = Date.valueOf("1987-08-08");
        }

        testResult(new Complex(),
                "create table multiple_types (thatid int, otherid int, field varchar(100), somearray varchar(100) array, created date)",
                "insert into multiple_types (thatid, otherid, field, somearray, created) values (1, 2, 'value', array['value1', 'value2'], '1987-08-08')",
                "select * from multiple_types"
                );
        jannex.query("insert into multiple_types (thatid, otherid, field, somearray, created) values (:thatId, :otherId, :field, :someArray, :created)")
                .set("thatId", 3)
                .set("otherId", 3)
                .set("field", "value")
                .set("someArray", Arrays.<Object>asList("a", "b"))
                .execute();
        List<Complex> cs = jannex.query("select * from multiple_types").fetch(Complex.class).toList();
        System.out.println(cs);
    }


//    static class Annotations {
//        @Id("id")
//        int idThingy;
//        @Column()
//        String overlyComplicatedName;
//    }
//
//    static class Joined {
//        int id;
//        String name;
//        Basic basic;
//
//        Joined(int id, String name, Basic basic) {
//            this.id = id;
//            this.name = name;
//            this.basic = basic;
//        }
//
//        @Override
//        public boolean equals(Object o) {
//            if (this == o) return true;
//            if (o == null || getClass() != o.getClass()) return false;
//            Joined joined = (Joined) o;
//            return id == joined.id && basic.equals(joined.basic) && name.equals(joined.name);
//        }
//    }
//
//    static class JoinedJoined {
//        int id;
//        String name;
//        Joined joined;
//
//        JoinedJoined(int id, String name, Joined joined) {
//            this.id = id;
//            this.name = name;
//            this.joined = joined;
//        }
//
//        @Override
//        public boolean equals(Object o) {
//            if (this == o) return true;
//            if (o == null || getClass() != o.getClass()) return false;
//            JoinedJoined joinedJoined = (JoinedJoined) o;
//            return id == joinedJoined.id && this.joined.equals(joinedJoined.joined) && name.equals(joinedJoined.name);
//        }
//    }
//
//    static class DoubleJoined {
//        int id;
//        String name;
//        Joined joined1;
//        Joined joined2;
//
//        DoubleJoined(int id, String name, Joined joined1, Joined joined2) {
//            this.id = id;
//            this.name = name;
//            this.joined1 = joined1;
//            this.joined2 = joined2;
//        }
//
//        @Override
//        public boolean equals(Object o) {
//            if (this == o) return true;
//            if (o == null || getClass() != o.getClass()) return false;
//            DoubleJoined that = (DoubleJoined) o;
//            return id == that.id && joined1.equals(that.joined1) && joined2.equals(that.joined2) && name.equals(that.name);
//        }
//    }
//
//
//    static final MappingContext CONTEXT = new MappingContext(
//            new MappingFactory(
//                    Collections.<ExclusionStrategy>emptyList(), false, "", false,
//                    Modifier.STATIC | Modifier.TRANSIENT
//            ), true);
//
//
//    @Test
//    public void simpleMapping() {
//
//        MappingFactory mappingFactory = new MappingFactory(
//                Collections.<ExclusionStrategy>emptyList(), false, "", false,
//                Modifier.STATIC | Modifier.TRANSIENT
//        );
//        ResultSetHandlerFactory resultSetHandlerFactory = new ResultSetHandlerFactory(CONTEXT);
//
//        ResultSetHandler<Basic> handler = resultSetHandlerFactory.create(mappingFactory.create(Basic.class), null);
//
//        Basic basic = handler.handle(
//                new ResultRow(
//                        new Object[]{1, "test"},
//                        new String[]{"ID", "NAME"},
//                        new String[]{"basic", "basic"}
//                ));
//        assertEquals(1, basic.id);
//        assertEquals("test", basic.name);
//    }
//
//
//    @Test
//    public void joinedMapping() {
//
//        MappingFactory mappingFactory = new MappingFactory(
//                Collections.<ExclusionStrategy>emptyList(), false, "", false,
//                Modifier.STATIC | Modifier.TRANSIENT
//        );
//        ResultSetHandlerFactory resultSetHandlerFactory = new ResultSetHandlerFactory(CONTEXT);
//
//        Mapping<Joined> mapping = mappingFactory.create(Joined.class);
//        ResultSetHandler<Joined> handler = resultSetHandlerFactory.create(mapping, null);
//
//        Joined joined = handler.handle(new ResultRow(
//                new Object[]{1, "test", 2, "joined"},
//                new String[]{"ID", "NAME", "ID", "NAME"},
//                new String[]{"basic", "basic", "joinedbasic", "joinedbasic"}
//        ));
//
//        assertEquals(2, joined.id);
//        assertEquals("joined", joined.name);
//
//        Basic basic = joined.basic;
//        assertEquals(1, basic.id);
//        assertEquals("test", basic.name);
//    }
//
//    static class DoubleJoinedBasic {
//        int id;
//        String name;
//        Joined joined;
//    }
//
//
//    @Test
//    public void doubleJoinedMapping() {
//
//        MappingFactory mappingFactory = new MappingFactory(
//                Collections.<ExclusionStrategy>emptyList(), false, "", false,
//                Modifier.STATIC | Modifier.TRANSIENT
//        );
//        ResultSetHandlerFactory resultSetHandlerFactory = new ResultSetHandlerFactory(CONTEXT);
//
//        Mapping<DoubleJoinedBasic> mapping = mappingFactory.create(DoubleJoinedBasic.class);
//        ResultSetHandler<DoubleJoinedBasic> handler = resultSetHandlerFactory.create(mapping, null);
//
//        DoubleJoinedBasic doubleJoinedBasic = handler.handle(new ResultRow(
//                new Object[]{1, "test", 2, "joined", 3, "doubleJoined"},
//                new String[]{"ID", "NAME", "ID", "NAME", "ID", "NAME"},
//                new String[]{"basic", "basic", "joinedbasic", "joinedbasic", "DoubleJoinedBasic", "DoubleJoinedBasic"}
//        ));
//
//        assertEquals(3, doubleJoinedBasic.id);
//        assertEquals("doubleJoined", doubleJoinedBasic.name);
//
//        Joined joined = doubleJoinedBasic.joined;
//        assertEquals(2, joined.id);
//        assertEquals("joined", joined.name);
//
//        Basic basic = doubleJoinedBasic.joined.basic;
//        assertEquals(1, basic.id);
//        assertEquals("test", basic.name);
//    }
//
//    static class DoubleBasic {
//        int id;
//        String name;
//        @Column(table = "basic1")
//        Basic basic1;
//        @Column(table = "basic2")
//        Basic basic2;
//    }
//
//    @Test
//    public void todo() {
//        MappingFactory mappingFactory = new MappingFactory(
//                Collections.<ExclusionStrategy>emptyList(), false, "", false,
//                Modifier.STATIC | Modifier.TRANSIENT
//        );
//        ResultSetHandlerFactory resultSetHandlerFactory = new ResultSetHandlerFactory(CONTEXT);
//
//        Mapping<DoubleBasic> mapping = mappingFactory.create(DoubleBasic.class);
//        ResultSetHandler<DoubleBasic> handler = resultSetHandlerFactory.create(mapping, null);
//
//        DoubleBasic doubleBasic = handler.handle(new ResultRow(
//                new Object[]{1, "test", 2, "joined", 3, "doubleJoined"},
//                new String[]{"ID", "NAME", "ID", "NAME", "ID", "NAME"},
//                new String[]{"basic1", "basic1", "DoubleBasic", "DoubleBasic", "Basic2", "Basic2"}
//        ));
//
//        assertEquals(2, doubleBasic.id);
//        assertEquals("joined", doubleBasic.name);
//
//    }
}
